import {Component} from "vue"
import _ from "lodash"
import {getCmp, registeVjsonComponent} from "./CmpStore";

export type PropertyDefine = {
    /**
     * 属性框中的顺序
     */
    order: number

    /**
     * 属性描述
     */
    desc: string

    /**
     * 属性编辑器
     */
    editor: string

    /**
     * 如果属性编辑器是 buttonGroup 或 combo 时，取值范围
     */
    options: {
        /**
         * 下拉选项文本
         */
        label: string

        /**
         * 下拉选项的值
         */
        value: any
    }[]

    /**
     * 属性是否允许绑定
     */
    bindable: boolean

    /**
     * 属性所属分组
     */
    category: string
}

export interface VjsonEventDefine {
    /**
     * 顺序(显示在开发平台中的)
     */
    order: number

    /**
     * 事件描述
     */
    desc: string

    /**
     * 事件类别
     */
    category: string
}

export interface VjsonMethodDefine {
    /**
     * 顺序
     */
    order: number

    /**
     * 方法描述
     */
    desc: string

    /**
     * 方法类别
     */
    category: string

    args: {
        /**
         * 参数名称
         */
        name: string

        /**
         * 参数描述
         */
        desc: string
    }[]

    result: {
        /**
         * 方法返回值描述
         */
        desc: string
    }
}

export interface VjsonPropertyDefineObject {
    /**
     * 属性名称
     */
    [PropertyName: string]: PropertyDefine
}

export interface VjsonEventDefineObject {
    /**
     * 事件名称
     */
    [EventName: string]: VjsonEventDefine
}

export interface VjsonMethodDefineObject {
    /**
     * 方法名称
     */
    [MethodName: string]: VjsonMethodDefine
}

export type DragGroupType =
/**
 * 表单字段组件, 只能往 form 和 toolbar 的子元素中拖拽
 */
    'fieldGroup' |

    /**
     * 大组件, 只能往基本大容器( module/dialog/split/tabs )中拖拽
     */
    'rowGroup' |

    /**
     * 表格字段组件，只能往 grid 的字段元素中拖拽
     */
    'columnGrop' |

    /**
     * 元素组，只能往 list 的子元素中拖拽
     */
    'elGroup'

export interface ToolboxDefine {
    /**
     * 内部使用
     */
    _cmpTag?: string

    /**
     * 工具栏分组
     */
    category?: string

    /**
     * 拖拽分组
     */
    dragGroup?: DragGroupType

    /**
     * 工具栏显示顺序, 越小越靠前
     */
    order?: number

    icon?: string

    /**
     * 从工具栏拖拽出来时生成代码的模板
     */
    onCreate?: (from: string) => string
}

export interface DefineVjsonComponentOptions<P extends VjsonPropertyDefineObject, E extends VjsonEventDefineObject, M extends VjsonMethodDefineObject> {
    /**
     * 组件树形的默认值
     */
    defaults?: any

    /**
     * 设计时组件
     */
    designer: Component | string

    /**
     * 运行时组件
     */
    runtime: Component | string

    /**
     * 工具箱定义
     */
    Toolbox?: ToolboxDefine[]

    /**
     * 用于 app.use() 注册组件
     */
    install: (Vue: any) => void

    /**
     * 组件的属性描述
     */
    properties?: P

    /**
     * 组件的事件描述
     */
    events?: E

    /**
     * 组件的方法描述
     */
    methodList?: M
}

type CopyPropertyNameOfAny<P> = {
    [K in keyof P]?: any
}

type CopyPropertyNameOfFuncable<P> = {
    [K in keyof P]?: string | Function
}

export interface VjsonDefine<P, E> extends CopyPropertyNameOfAny<P> {
    bind?: CopyPropertyNameOfFuncable<P>
    listeners?: CopyPropertyNameOfFuncable<E>
}

export class VjsonComponent<P extends VjsonPropertyDefineObject, E extends VjsonEventDefineObject, M extends VjsonMethodDefineObject> {
    cmpTag: string
    option: DefineVjsonComponentOptions<P, E, M>

    constructor(cmpTag: string, options: DefineVjsonComponentOptions<P, E, M>) {
        this.cmpTag = cmpTag
        this.option = options

        this.vjsonEmit = {} as any
        _.forOwn(this.option.events, (value, eventName) => {
            this.vjsonEmit[eventName] = ((sender: Component, ...args: any[]) => {
                console.log('runEvent', eventName, (sender as any).vjson, ...args)
            }) as any
        })

        // 为每个 toolbox 添加 _cmpTag
        _.forEach(this.option.Toolbox, toolbox => {
            toolbox._cmpTag = this.cmpTag
        })
    }

    loadVjsonWithDefault(vueInstance: any): VjsonDefine<P, E> {
        return _.defaults(vueInstance.vjson, this.option.defaults)
    }

    install(Vue) {
        if (typeof this.option.install === 'function') {
            this.option.install(Vue)
        }
    }

    vjsonEmit: {
        [k in keyof E]: (sender: Component, ...args: any[]) => any
    }
}

export default function defineVjsonComponent<P extends VjsonPropertyDefineObject, E extends VjsonEventDefineObject, M extends VjsonMethodDefineObject>(cmpTag: string, option: DefineVjsonComponentOptions<P, E, M>): VjsonComponent<P, E, M> {
    if (getCmp(cmpTag)) {
        throw new Error(`组件${cmpTag}已经注册过`)
    }
    const cmp = new VjsonComponent<P, E, M>(cmpTag, option)
    registeVjsonComponent(cmp)
    return cmp
}